//
//  WCExtractSetArray.m
//  WebClipper
//
//  Created by Daniel Braun on 07/04/07.
//  Copyright 2007 Daniel Braun http://page-appart.net, http://braun.daniel.free.fr All rights reserved.
//

#import "WCExtractSetArray.h"
#import "WCWebExtractSet.h"
#import "DictionaryWrapper.h"
#import "ExtractAstNode.h"
#import "DBLexer.h"
#import "DBParser.h"
#import "DBRegex.h"

@implementation WCExtractSetArray

+ (id) extractSetArrayFromSaveDictionnary:(NSDictionary *)info
{
	id esetsa=[info objectForKey:@"esets"];
	if ([esetsa isKindOfClass:[NSNull class]]) {
		return nil;
	} else if ([esetsa isKindOfClass:[WCExtractSetArray class]]) {
		[esetsa update];
		return esetsa;
	} else if ([esetsa isKindOfClass:[NSArray class]]) {
		// compatibility with pre-20070407 files
		return [[[WCExtractSetArray alloc]initWithArray:esetsa]autorelease];
	} else {
		NSAssert(0, @"bad class for esets");
		return nil;
	}
}

- (id) init {
	self = [super init];
	if (self != nil) {
		_a=[[NSMutableArray arrayWithCapacity:10]retain];
	}
	return self;
}
- initWithArray:(NSArray *)a
{
	self = [super init];
	if (self != nil) {
		_a=[a mutableCopy];
		[self update];
	}
	return self;
}

- (void) dealloc {
	[_a release];
	[_otherVariables release];
	[super dealloc];
}
- (id) initWithCoder:(NSCoder *) coder
{
	//self=[super initWithCoder:coder];
	self=[super init];
	if ([coder allowsKeyedCoding]) {
		[_a release];
		_a=[[coder decodeObjectForKey:@"_a"]retain];
		_otherVariables=[[coder decodeObjectForKey:@"_otherVariables"]retain];
		
		[self update];
	}
	return self;
}

- (void)encodeWithCoder:(NSCoder *)coder
{
	//[super encodeWithCoder:coder];
	if ( [coder allowsKeyedCoding] ) {
		[coder encodeObject:_a forKey:@"_a"];
		[coder encodeObject:_otherVariables forKey:@"_otherVariables"];
	}
}

- (void) update
{
	unsigned int i, count = [_a count];
	for (i = 0; i < count; i++) {
		WCWebExtractSet * obj = [_a objectAtIndex:i];
		NSAssert([obj isKindOfClass:[WCWebExtractSet class]], @"bad class");
		if (i) [obj setPrevExtractSet:[_a objectAtIndex:i-1]];
		if (i<count-1) [obj setNextExtractSet:[_a objectAtIndex:i+1]];
	}
}
#if 0
- (NSMutableArray *)mutableArrayValueForKey:(NSString *)key
{
	return nil;
}
- (NSMutableArray *)mutableArrayValueForKeyPath:(NSString *)keyPath
{
	return nil;
}
- (NSMutableSet *)mutableSetValueForKey:(NSString *)key
{
	return nil;
}
- (NSMutableSet *)mutableSetValueForKeyPath:(NSString *)keyPath
{
	return nil;
}
#endif
// transparently forward methods and kvc/kvo to _a array object
#if 0
- (unsigned int)count {
	// implementation specific code
	return [_a count];
}
- (unsigned int)countOf {
	// implementation specific code
	return [_a count];
}

- (BOOL)containsObject:(id)anObject 
{
}
- (void)getObjects:(id *)aBuffer
{
}
- (void)getObjects:(id *)aBuffer range:(NSRange)aRange
{
}

- (unsigned)indexOfObject:(id)anObject
{
}

- (unsigned)indexOfObject:(id)anObject inRange:(NSRange)range
{
}

- (id)lastObject
{
}

- (void)addObserver:(NSObject *)anObserver toObjectsAtIndexes:(NSIndexSet *)indexes forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context
{
}
- (void)addObserver:(NSObject *)anObserver forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context
{
}

- (NSArray *)objectsAtIndexes:(NSIndexSet *)indexes
{
}
- (NSEnumerator *)objectEnumerator
{
}



- (WCWebExtractSet *)objectAtIndex:(unsigned int)index {
	// implementation specific code
	return [_a objectAtIndex:index];
}



+ (BOOL)accessInstanceVariablesDirectly;
{
	return YES;
}
- (void)addObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath options:(NSKeyValueObservingOptions)options context:(void *)context;
{
	NSLog(@"*** Ignoring *** %@ for '%@' (of %@ with %#x in %p).\n", NSStringFromSelector(_cmd), keyPath, observer, options, context);
	return;
}
- (void)removeObserver:(NSObject *)observer forKeyPath:(NSString *)keyPath;
{
	NSLog(@"*** Ignoring *** %@ for '%@' (of %@).", NSStringFromSelector(_cmd), keyPath, observer);
}

- (void) doesNotRecognizeSelector:(SEL) aSelector
{
}
#endif

+ (BOOL)useStoredAccessor
{
 	return YES;
}
 
- (unsigned int)countOfArray {
	// implementation specific code
	return [_a count];
}

- (id )objectInArrayAtIndex:(unsigned int)index {
	// implementation specific code
	return [_a objectAtIndex:index];
}

- (void)insertObject:(id )obj
    inArrayAtIndex:(unsigned int)index
{
	[_a insertObject:obj atIndex:index];
	return;
}

- (void)removeObjectFromArrayAtIndex:(unsigned int)index 
{
	[_a removeObjectAtIndex:index];
	return;
}
- (NSString *)descriptionWithLocale:(NSDictionary *)locale
{
	return [self description];
}

- (void)forwardInvocation:(NSInvocation *)anInvocation
{
	if ([_a respondsToSelector:[anInvocation selector]]) {
		[anInvocation invokeWithTarget:_a];
		[self update];
	} else {
		[super forwardInvocation:anInvocation];
	}
}

+ (BOOL)respondsToSelector:(SEL)aSelector
{
	return YES;
}
- (BOOL) respondsToSelector:(SEL)aSelector
{
	if ( [super respondsToSelector:aSelector] )
		return YES;
	else if ([_a respondsToSelector:aSelector]) {
		return YES;
	}
	return NO;
}





- (NSMethodSignature*) methodSignatureForSelector:(SEL)selector
{
	NSMethodSignature *sig;
	sig=[_a methodSignatureForSelector:selector];
	if (!sig) return [super methodSignatureForSelector:selector];
	return sig;
}
#if 0
- (id)valueForKeyPath:(NSString *)keyPath
{
	if ([keyPath isEqualToString:@"array"]) return _a;
	return [_a valueForKeyPath:keyPath];
}
- (id) valueForKey:(NSString *)k
{
	if ([k isEqualToString:@"array"]) return _a;
	return [_a valueForKey:k];
}

- (void)setValue:(id)value forKeyPath:(NSString *)keyPath
{
	[_a setValue:value forKeyPath:keyPath];
}
- (void)setValue:(id)value forKey:(NSString *)k
{
	[_a setValue:value forKey:k];
}

#endif
- (id)valueForUndefinedKey:(NSString *)key
{
	return [_a valueForKey:key];
}

#if 0
- (void)setValue:(id)value forKeyPath:(NSString *)keyPath
{
	[_a setValue:value forKeyPath:keyPath];
}
#endif
- (void)setValue:(id)value forUndefinedKey:(NSString *)key
{
	if ([key isEqualToString:@"array"]) {
		if (![value isKindOfClass:[NSMutableArray class]]) {
			// caution : mutability of array is not really checked here
			// this should handle NSKeyValueArray however
			//value=[NSMutableArray arrayWithArray:value];
			return;
		}
		if (_a != value) {
			[value retain];
			[_a release];
			_a=value;
			[self update];
		}
	} else {
		[_a setValue:value forKey:key];
	}
	[self update];
}

- _evaluate:(id)v key:(NSString *)name withDictionary:(NSDictionary *)vars
{	
	if (!vars) vars=[self extractVariableDictionary];
	if ([v isKindOfClass:[NSString class]]) {
		NSString *s=(NSString *)v;
		if ([s hasPrefix:@"="]) {
			//NSString *s1=[s substringFromIndex:1];
			if (!parser) [self setupParser];
			[parser reset];
			[parser parseTokens:
				[lexer parseString:s]];
			ExtractAstNode *node=(ExtractAstNode *)[parser ast];
			[self changeUserVariable:name toValue:node];
			id r;
			NSLog(@"got ast %@\n", node);
			r=[node evaluateWithDictionary:vars];
			return r;
		}
		return s;
	} else if ([v isKindOfClass:[ExtractAstNode class]]) {
		ExtractAstNode *n=(ExtractAstNode *)v;
		id r;
		r=[n evaluateWithDictionary:vars];
		return r;
	} else return v;
}

- (NSDictionary *) parsedUserVariablesWithDictionary:(NSDictionary *)vars
{
	NSMutableDictionary *d=[NSMutableDictionary dictionaryWithCapacity:10];
	unsigned int i, count = [_otherVariables count];
	for (i = 0; i < count; i++) {
		DictionaryWrapper * da = [_otherVariables objectAtIndex:i];
		id k=[da key];
		id v=[da value];
		v=[self _evaluate:v key:k withDictionary:vars];
		if (v) [d setObject:v forKey:k];
	}
	return d;
}


- (NSDictionary *) otherVariables
{
	return [DictionaryWrapper dictionaryFromArray:[self arrayVariables]];
}

- (NSMutableArray *) arrayVariables
{
	if (!_otherVariables) _otherVariables=[[NSMutableArray arrayWithCapacity:10]retain];
	return _otherVariables;
}



- (void) setArrayVariables:(NSMutableArray *)a
{
	[_otherVariables release]; 
	_otherVariables=[a retain];
}
- (NSArray *) extractVariables
{
	NSMutableArray *ea=[NSMutableArray arrayWithCapacity:10];
	unsigned int i, count = [_a count];
	for (i = 0; i < count; i++) {
		WCWebExtractSet * we = [_a objectAtIndex:i];
		NSArray *v=[we extractVariables ];
		if (v) [ea addObjectsFromArray:v];
	}
	return ea;
}
- (NSMutableArray *) userVariables
{
	NSMutableArray *oa=[NSMutableArray arrayWithCapacity:10];
	unsigned int i, count = [_otherVariables count];
	for (i = 0; i < count; i++) {
		DictionaryWrapper * obj = [_otherVariables objectAtIndex:i];
		[oa addObject:[obj key]];
	}
	return oa;
}
- (NSMutableArray *) allVariables
{
	NSMutableArray *a=[self userVariables];
	[a addObjectsFromArray:[self extractVariables]];
	return a;
}
- (NSDictionary *) extractVariableDictionary
{
	NSDictionary *d;
	d=[DictionaryWrapper dictionaryFromArray:_otherVariables];
	return d;
}

- (id) userVariableValue:(NSString *)name  withDictionary:(NSDictionary *)vars
{
	unsigned int i, count = [_otherVariables count];
	for (i = 0; i < count; i++) {
		DictionaryWrapper * da = [_otherVariables objectAtIndex:i];
		NSString *key=[da key];
		if ([key isEqualToString:name]) {
			id v=[da value];
			return [self _evaluate:v key:key withDictionary:vars];
		}
	}
	return @"v?";
}

- (void)  changeUserVariable:(NSString *)name toValue:(id)v
{
	unsigned int i, count = [_otherVariables count];
	for (i = 0; i < count; i++) {
		DictionaryWrapper * da = [_otherVariables objectAtIndex:i];
		if ([[da key]isEqualToString:name]) {
			[da setValue:v];
			return;
		}
	}
}


- (void) setupParser
{
	lexer=[[DBLexer alloc]init];
		
	[lexer addToken:[NSCharacterSet characterSetWithCharactersInString:@" \t\n\r"] value:-1];
	[lexer addToken:[DBRegex regexWithPattern:@"[0-9]*[0-9.][0-9]*"] value:TOK_NUM ast:@"$"];
	[lexer addToken:[DBRegex regexWithPattern:@"[+-]"] value:TOK_ADDOP ast:@"LR"];
	[lexer addToken:[DBRegex regexWithPattern:@"[\\*/]"] value:TOK_MULTOP  ast:@"LR"];
	[lexer addToken:[DBRegex regexWithPattern:@"="] value:TOK_EQ  ast:@"R"];
	[lexer addToken:[DBRegex regexWithPattern:@"[A-Za-z][A-Za-z.-_0-9]*"] value:TOK_ID  ast:@"$"];


	/*
	 eexpr ::= "=" expr
	 expr  ::= multexpr ( addop multexpr )*;
	 multexpr ::= expr1 (multop expr1)*
	 expr1 ::= num | id
	 */
	DBParserRule *expr1=[DBParserRuleNamed ruleNamed:@"expr1" action:(SEL)0 rule:
		[DBParserRuleChoice choice:
			[DBParserRuleTerminal terminalRule:TOK_NUM action:(SEL)0],
			[DBParserRuleTerminal terminalRule:TOK_ID action:(SEL)0], nil], nil];
	DBParserRule *multexpr=[DBParserRuleNamed ruleNamed:@"multexpr" action:(SEL)0 rule:
		expr1, [ DBParserRuleOptional optionalMany:
			[DBParserRuleTerminal terminalRule:TOK_MULTOP action:(SEL)0],
			expr1,nil],nil];
	DBParserRule *expr=[DBParserRuleNamed ruleNamed:@"expr" action:(SEL)0 rule:
		multexpr, [ DBParserRuleOptional optionalMany:
			[DBParserRuleTerminal terminalRule:TOK_ADDOP action:(SEL)0],
			multexpr,nil],nil];
	DBParserRule *eexpr=[DBParserRuleNamed ruleNamed:@"eexpr" action:(SEL)0 rule:
		[DBParserRuleTerminal terminalRule:TOK_EQ action:(SEL)0],
		expr, 
		/* [DBParserRuleTerminal terminalRule:-99 action:(SEL)0],*/
		nil];

	parser=[[DBParser alloc]init];
	[parser setRule:eexpr];
	[parser setNodeClass:[ExtractAstNode class]];
}

- (WCWebExtractSet *) esetForPage:(NSString *)page
{
	NSUInteger i, count = [_a count];
	for (i = 0; i < count; i++) {
		WCWebExtractSet * es = [_a objectAtIndex:i];
		if ([[es name] isEqualToString:page]) return es;
	}
	return nil;
}
@end
